/*** version :2005-1228-2114 ****/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <memory.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <stdarg.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netdb.h>
#include <unistd.h> 

#include <pwd.h>
#include <termios.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <time.h>
#include <pthread.h>


#include "common.h"
#include "tunnet.h"
#include "global.h"

/*
extern int f_report ;
extern int f_debug;
extern int seconds_timeout;
*/
const char *digits    = "0123456789";
const char *dotdigits = "0123456789.";


/*
void
err_sys(const char *fmt, ...)
{
        va_list         ap;

        va_start(ap, fmt);
        err_doit(1, LOG_ERR, fmt, ap);
        va_end(ap);
        exit(1);
}

void
err_quit(const char *fmt, ...)
{
        va_list         ap;

        va_start(ap, fmt);
        err_doit(0, LOG_ERR, fmt, ap);
        va_end(ap);
        exit(1);
}
static void
err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
        int             errno_save, n;
        char    buf[MAXLINE];

        errno_save = errno;             
#ifdef  HAVE_VSNPRINTF
        vsnprintf(buf, sizeof(buf), fmt, ap); 
#else
        vsprintf(buf, fmt, ap);              
#endif
        n = strlen(buf);
        if (errnoflag)
                snprintf(buf+n, sizeof(buf)-n, ": %s", strerror(errno_save));
        strcat(buf, "\n");

        if (daemon_proc) {
                syslog(level, buf);
        } else {
                fflush(stdout);        
                fputs(buf, stderr);
                fflush(stderr);
        }
        return;
}

*/
void
debug( const char *fmt, ... )
{
    time_t t;
    struct tm *gt;
    char timestamp[128];
    va_list args;

    if ( f_debug ) {
    	time(&t);
    	gt=(struct tm *)localtime(&t);
    	sprintf(timestamp,"%4d-%02d-%02d-%02d-%02d-%02d: ",
            1900+gt->tm_year,1+gt->tm_mon, gt->tm_mday,
            gt->tm_hour,gt->tm_min, gt->tm_sec);

        va_start( args, fmt );

	pthread_mutex_lock(&mutex_info);
        fprintf(info_log, "DEBUG: ");
        fprintf(info_log,timestamp);
        vfprintf(info_log, fmt, args );
	fflush(info_log);
	pthread_mutex_unlock(&mutex_info);

        va_end( args );
    }
}

void
fatal( const char *fmt, ... )
{
    va_list args;
    va_start( args, fmt );
    fprintf(info_log, "FATAL: ");
    vfprintf( info_log, fmt, args );
    va_end( args );
    return ;
}

void
report_bytes( char *prefix, char *buf, int len )
{
    fprintf(info_log, "%s", prefix );
    while ( 0 < len ) {
        fprintf( info_log, " %02x", *(unsigned char *)buf);
        buf++;
        len--;
    }
    fprintf(info_log, "\n");
    return;
}

int
atomic_out( SOCKET s, void *buf, int size )
{
    int ret, len;

    assert( buf != NULL );
    assert( 0<=size );
    /* do atomic out */
    ret = 0;
    while ( 0 < size ) {
        //len = send( s, buf+ret, size, 0 );
        len = write( s, buf+ret, size );
        if ( len == -1 ){
            debug("atomic_out() failed to send(), %d:%s\n", socket_errno(),strerror(errno));
            return -1;
        }
        ret += len;
        size -= len;
    }
    if (f_report) {
        fprintf(info_log,"atomic_out() [%d bytes]:", ret);
        report_bytes(">>>", buf, ret);
    }
    return ret;
}
/*
atomic_in_errorOut(SOCKET s , char *buf, int size){
     int res;
     if((res=atomic_in(s,buf,size)) !=size){
	debug("atomic_in_errorOut:%d\n",res);
	return -1;	
     }
}
*/
int
atomic_in( SOCKET s, void *buf, int size )
{
    int ret, len;

    assert( buf != NULL );
    assert( 0<=size );

    /* do atomic in */
    ret = 0;
    while ( 0 < size ) {
        //len = recv( s, buf+ret, size, 0 );
        len = read( s, buf+ret, size );
        if ( len == -1 ) {
            debug("atomic_in() failed to read(), %d:%s\n", socket_errno(),strerror(errno));
            return -1;
        } else if ( len == 0 ) {
            //debug( "atomic_in() read len=0. Connection closed by peer.\n");
            return ret;
        }
        ret += len;
        size -= len;
    }
    if (f_report) {
        fprintf(info_log, "atomic_in() [%d bytes]:", ret);
        report_bytes("<<<", buf, ret);
    }
    return ret;
}


// Non-Blocking Connect
int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int *timeout)
{
  int n, error;
  time_t start, end;
  socklen_t len;
  fd_set rset, wset;
  struct timeval tval;
  int flags,flags1;

  if (*timeout ){
   ////////////// make sockfd nonblocking
           if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) {
              	perror("make_nonblocking: fcntl");
		return -1;
            }
            if (fcntl(sockfd, F_SETFL, flags | O_NDELAY) == -1) {
                perror("make_nonblocking: fcntl");
		return -1;
            } //if 
	
    } //if(*timeout)

  error = 0;
  if ((n = connect(sockfd, (const struct sockaddr *) saptr, salen)) < 0)
    if (errno != EINPROGRESS)
    {
	fprintf(info_log, " connect error(connect_nonb) strerror(errno)\n");
    	return -1;
     }

  /* connect completed immediately or we did a blocking connect  */
  if (!timeout || !*timeout || n == 0)
    goto done;

  FD_ZERO(&rset);
  FD_SET(sockfd, &rset);
  wset = rset;
  tval.tv_sec = *timeout;
  tval.tv_usec = 0;
  time(&start);


  if ((n = select(sockfd + 1, &rset, &wset, NULL, &tval)) == 0) {
    //close(sockfd);  /* timeout,  the target may not be reached  */
    errno = ETIMEDOUT;
    debug("connect_nonb() timeout\n");
    return -1;
  }

  time(&end);

  if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
    len = sizeof(error);
    //if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (__ptr_t) &error, &len) < 0){
    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*) &error, &len) < 0){
      fprintf(info_log, " getsockopt error(connect_nonb)\n");
      return -1;    /* Solaris pending error */
    }
  } else{
    fprintf(info_log, " FD_ISSET error(connect_nonb)\n");
    return -1;
  }

done:

  //restore the socks flags, reset it  blocking;
  if (fcntl(sockfd, F_SETFL, flags) == -1) {
        perror("make_nonblocking: fcntl while restore socket flags");
    	return -1;
   }
                                       
   if (error) {
    //debug("connect failed \n");
    errno = error;
    return -1;
  }
  return 0;
}

unsigned long shuffle_bytes(int client, int server) {
	fd_set fds;
	unsigned char buf[BUF_SIZE];
	int c;
	unsigned long total=0;

	while(1) {
		FD_ZERO(&fds);
		FD_SET(client,&fds);
		FD_SET(server,&fds);
		if(select(getdtablesize(),&fds,NULL,NULL,NULL)<0) {
			/* hmm - error */
			fprintf(info_log,"select() failed. Aborting.\n");
			break;
		}
		if(FD_ISSET(client,&fds)) {
			c = read(client,&buf,BUF_SIZE);
			if(c<0) {
				//debug("client error (%s)\n",strerror(errno));
				break;
			}
			if(c==0) {
				//debug("client closed connection\n");
				break;
			}
			if(write(server,&buf,c)!=c) {
				//debug("WARNING: couln't write enough bytes to server (1)\n");
			}
			//debug("write it to server done\n");
			total+=c;
		}
		if(FD_ISSET(server,&fds)) {
			c = read(server,&buf,BUF_SIZE);
			//buf[c]=0;
			//debug("Read from server: %s\n",buf);
			//c = recv(server,&buf,BUF_SIZE,0);
			if(c<0) {
				//debug("server error (%s)\n",strerror(errno));
				break;
			}
			if(c==0) {
				//debug("server closed connection\n");
				break;
			}
			if(write(client,&buf,c)!=c) {
				//debug("WARNING: couln't write enough bytes to client(2)\n");
			}
			//debug("write it to client done\n");
			total+=c;
		}
	}
	//debug("shuffle returned\n");
	return total;
}


unsigned int encodeXOR(char *buf,int size, int key)
{
	int i;

	for(i=0;i<size;i++)
	{
		buf[i]=buf[i] ^ 0x55;
	}
	return i;
}
/*
	Relay data between to sockets,  XOR it with 0x33
*/

unsigned long shuffle_bytes_encode(int client, int server) {
        fd_set fds;
        unsigned char buf[BUF_SIZE];
        int c;
        unsigned long total=0;

        while(1) {
                FD_ZERO(&fds);
                FD_SET(client,&fds);
                FD_SET(server,&fds);
                if(select(getdtablesize(),&fds,NULL,NULL,NULL)<0) {
                        /* hmm - error */
                        fprintf(info_log,"select() failed. Aborting.\n");
                        break;
                }
                if(FD_ISSET(client,&fds)) {
                        c = read(client,&buf,BUF_SIZE);
                        if(c<0) {
                                //debug("client error (%s)\n",strerror(errno));
                                break;
                        }
                        if(c==0) {
                                //debug("client closed connection\n");
                                break;
                        }
                        report_bytes("read from client:",buf,c>10?10:c);
			encodeXOR(buf,c,0x33);
                        report_bytes("write to  server:",buf,c>10?10:c);
			//debug("Write to server:%s\n", buf);

                        if(write(server,&buf,c)!=c) {
                                //debug("WARNING: couln't write enough bytes to server (1)\n");
                        }
                        total+=c;
                }
                if(FD_ISSET(server,&fds)) {
                        c = read(server,&buf,BUF_SIZE);
                        //report_bytes("read from server:",buf,c>10?:c);
                        //debug("Read from server: %s\n",buf);
                        //c = recv(server,&buf,BUF_SIZE,0);
                        if(c<0) {
                                //debug("server error (%s)\n",strerror(errno));
                                break;
                        }
                        if(c==0) {
                                //debug("server closed connection\n");
                                break;
                        }
			encodeXOR(buf,c,0x33);
                        if(write(client,&buf,c)!=c) {
                                //debug("WARNING: couln't write enough bytes to client(2)\n");
                        }
                        //debug("write it to client done\n");
                        total+=c;
                }
        }
        //debug("shuffle returned\n");
        return total;
}
/***************************************************************************** 
*** Get the IP address of interface ifName in address family addrFamily  
*** If succeed, return 0, and the result will be stored in a string  'ipAddress'
*** ipAddress must NOT be NULL, and the length of ipAddress is pointed with
*** 'len_ip';
*** If failed , return -1;
********************************************************************************/ 
int get_ip_if(char * ifName, int addrFamily, char *ipAddress, int len_ip){

	int sockfd, res ,len, result;
	char * buf, *ptr;// str[128];
	struct ifreq  *ifr;
	struct sockaddr_in * sinptr;
	in_addr_t addr;
	struct ifconf ifc;
	struct sockaddr_in6 * sin6ptr;

	result=-1;

	if((ipAddress ==NULL ) || (len_ip ==0)|| (ifName ==NULL)) return result;

	sockfd=socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd<0){
		fprintf(info_log, "socket error in get_ip_if()\n");
		return result;
	}

	len=MAX_INTERFACE * sizeof(struct ifreq);
	buf=(char *) malloc(len);
	ifc.ifc_len=len;
	ifc.ifc_buf=buf;

	res=ioctl(sockfd, SIOCGIFCONF, &ifc);
	if(res <0){
		fprintf(info_log, "ioctl error in get_ip_if() \n");
		return result;
	}
	for(ptr=buf; ptr<buf+ifc.ifc_len;){
		ifr=(struct ifreq *) ptr;
		/*
		if((ifName==NULL) && (ifr->ifr_addr.sa_family == addrFamily)
			&& (strcmp(ifr->ifr_name, "lo")) )
		{
			addr=((struct sockaddr_in *) &(ifr->ifr_addr))->sin_addr.s_addr;
                        inet_ntop(ifr->ifr_addr.sa_family, &addr, ipAddress, len_ip);
                        result=0;
                        break;
		}
		else if(ifName !=NULL) {
		*/
		   if( (ifr->ifr_addr.sa_family == addrFamily)
		    	&& !strcmp(ifr->ifr_name, ifName) )
		   {
			addr=((struct sockaddr_in *) &(ifr->ifr_addr))->sin_addr.s_addr;
                        inet_ntop(ifr->ifr_addr.sa_family, &addr, ipAddress, len_ip);
			result=0;
			break;
		   }
		//}
		switch (ifr->ifr_addr.sa_family ==AF_INET){
			case AF_INET6 :
				//len=sizeof(struct sockaddr_in6);
			break;    
			case AF_INET :
			default:
				len=sizeof(struct sockaddr);
			break;    
		}	
		ptr+=sizeof(ifr->ifr_name)+len;
	}

	free(buf);
	return result;
	
}

/** ifName is the returned ifname, ipAddress is the ip string  **/
int get_first_if(char * ifName, int addrFamily, char *ipAddress, int len_ip){

	int sockfd, res ,len, result;
	char * buf, *ptr;// str[128];
	struct ifreq  *ifr;
	struct sockaddr_in * sinptr;
	in_addr_t addr;
	struct ifconf ifc;
	struct sockaddr_in6 * sin6ptr;

	result=-1;

	if((ipAddress ==NULL ) || (len_ip ==0)) return result;

	sockfd=socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd<0){
		fprintf(info_log, "socket error in get_ip_if()\n");
		return result;
	}

	len=MAX_INTERFACE * sizeof(struct ifreq);
	buf=(char *) malloc(len);
	ifc.ifc_len=len;
	ifc.ifc_buf=buf;

	res=ioctl(sockfd, SIOCGIFCONF, &ifc);
	if(res <0){
		fprintf(info_log, "ioctl error in get_ip_if() \n");
		return result;
	}
	for(ptr=buf; ptr<buf+ifc.ifc_len;){
		ifr=(struct ifreq *) ptr;
		if( (ifr->ifr_addr.sa_family == addrFamily)
			&& (strcmp(ifr->ifr_name, "lo")) )
		{
			addr=((struct sockaddr_in *) &(ifr->ifr_addr))->sin_addr.s_addr;
                        inet_ntop(ifr->ifr_addr.sa_family, &addr, ipAddress, len_ip);
			strcpy(ifName,ifr->ifr_name);
                        result=0;
                        break;
		}
		switch (ifr->ifr_addr.sa_family ==AF_INET){
			case AF_INET6 :
				//len=sizeof(struct sockaddr_in6);
			break;    
			case AF_INET :
			default:
				len=sizeof(struct sockaddr);
			break;    
		}	
		ptr+=sizeof(ifr->ifr_name)+len;
	}

	free(buf);
	return result;
}


int get_if(char * ifName, int addrFamily, char *ipAddress, int len_ip){

        int sockfd, res ,len, result;
        char * buf, *ptr;// str[128];
        struct ifreq  *ifr;
        struct sockaddr_in * sinptr;
        in_addr_t addr;
        struct ifconf ifc;
        struct sockaddr_in6 * sin6ptr;

        result=-1;

        if((ipAddress ==NULL ) || (len_ip ==0)) return result;

        sockfd=socket(AF_INET, SOCK_DGRAM, 0);
        if(sockfd<0){
                debug("socket error in get_ip_if()\n");
                return result;
        }

        len=MAX_INTERFACE * sizeof(struct ifreq);
        buf=(char *) malloc(len);
        ifc.ifc_len=len;
        ifc.ifc_buf=buf;

        res=ioctl(sockfd, SIOCGIFCONF, &ifc);
        if(res <0){
                debug("ioctl error in get_ip_if() \n");
                return result;
        }
        for(ptr=buf; ptr<buf+ifc.ifc_len;){
                ifr=(struct ifreq *) ptr;
                if( (ifr->ifr_addr.sa_family == addrFamily) && (!strcmp(ifr->ifr_name, ifName)) )
                {
                        addr=((struct sockaddr_in *) &(ifr->ifr_addr))->sin_addr.s_addr;
                        inet_ntop(ifr->ifr_addr.sa_family, &addr, ipAddress, len_ip);
                        //strcpy(ifName,ifr->ifr_name);
                        result=0;
                        break;
                }
                switch (ifr->ifr_addr.sa_family ==AF_INET){
                        case AF_INET6 :
                                len=sizeof(struct sockaddr_in6);
                        break;
                        case AF_INET :
                        default:
                                len=sizeof(struct sockaddr);
                        break;
                }
                ptr+=sizeof(ifr->ifr_name)+len;
        }

        free(buf);
        return result;
}

//set sockopt and get sockopt
set_sockopt(int s,int sendBuf, int recvBuf)
{
	//int sendBuf=SEND_BUF_SIZE*10;
	//int recvBuf=RECV_BUF_SIZE*10;
	//int sendBuf=0;
	socklen_t opt_send_len=sizeof(sendBuf);
	//int recvBuf=0;
	socklen_t opt_recv_len=sizeof(recvBuf);
	int z=0;
		
	z=setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sendBuf,sizeof(sendBuf));
	//printf("Set SendBuf: %d bytes\n",sendBuf);
	z=setsockopt(s,SOL_SOCKET,SO_RCVBUF,&recvBuf,sizeof(recvBuf));
	//printf("Set recvBuf: %d bytes\n",recvBuf);
}

get_sockopt(int s)
{
	int sendBuf=0;
	socklen_t opt_send_len=sizeof(sendBuf);
	int recvBuf=0;
	socklen_t opt_recv_len=sizeof(recvBuf);
	int z=0;
	z=getsockopt(s,SOL_SOCKET,SO_SNDBUF,&sendBuf,&opt_send_len);
	//printf("Get SendBuf: %d bytes\n",sendBuf);
	z=getsockopt(s,SOL_SOCKET,SO_RCVBUF,&recvBuf,&opt_recv_len);
	//printf("Get recvBuf: %d bytes\n",recvBuf);

}

//Get the remote ip and port of  socket_fd, 
// return 0 if success; or -1 ;
// *ipAddress and *port are both in host byte order
int get_remote_socket_info(int socket_fd, struct in_addr *ipAddress,int * port)
{
	struct sockaddr_in peerAddr;
	int  addrLen ;

	if(!ipAddress || !port){
		return -1;
	}
	if( 0!= getpeername(socket_fd, (struct sockaddr*)&peerAddr, &addrLen) )
	{	
		debug("Error in common.c:get_remote_socket_info:%d, %s\n", socket_errno(),strerror(errno));
		return -1;
	}
	*port=ntohs(peerAddr.sin_port);
	ipAddress->s_addr=peerAddr.sin_addr.s_addr;
	return 0;
}

struct outer_thread_args{
	in_addr_t ip;
	int port_com;
	int port_local;	
};

void * outer_proccess(void * args);
void * inner_proccess(void * args);
void * shuffle_thread_proccess(void * args);


int openSockets[MAX_NEIGHBORS];
int pointer;

int reverse_connect_inner(in_addr_t  ipAddress,int port_com, int port_service)
{
//For node inside NAT: 
// 1. setup a client socket1 connect to port_com of remote ipAddress;
// 2. if success, setup a client socket2 to local service port port_service;
// 3. if success, relay between socket1 and socket2 ;
// 4. if success return 0; or -1 error;
	
       int result;

        struct in_addr inaddr;
        inaddr.s_addr=ntohl(ipAddress);
        debug("Begin reverse_connect_inner ,remote ip= %s  port_com= %d  port_local=%d \n",
              inet_ntoa(inaddr), ntohs(port_com),ntohs(port_service) );
        pthread_t tid;
        struct outer_thread_args * args;
        args=(struct outer_thread_args *) malloc(sizeof(struct outer_thread_args));
        args->ip=ipAddress;
        args->port_com=port_com;
        args->port_local=port_service;

        if( pthread_create(&tid,NULL, inner_proccess, (void *) args))
                return -1;

    return 0;
}
void * inner_proccess(void * args)
{
       int port_com,port_local;
        in_addr_t ipAddress;
        int result;
        int socket_com, socket_com2, socket_local;
        fd_set allset,rset;
	struct sockaddr_in saddr_com,saddr_com2,saddr_local, saddr_inner;
	int timeout=5;



        pthread_detach(pthread_self());
        ipAddress = ((struct outer_thread_args *) args) -> ip;
        port_com= ((struct outer_thread_args *) args) ->port_com;
        port_local= ((struct outer_thread_args *) args) ->port_local;
        free(args);

	socket_com = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        if(socket_com <0) {
                sys_log(info_log, "socket error (connect_socks4)\n");
                return ;
        }
        saddr_com.sin_family=AF_INET;
        saddr_com.sin_addr.s_addr=ipAddress; //htonl(nbr_ip);
        saddr_com.sin_port =port_com;

        //connect to outer port_com 
        result= connect_nonb(socket_com, (struct sockaddr *)&saddr_com, sizeof(saddr_com),&timeout);
        if ( result <0 ) {
            close (socket_com);
            return ;
        }

        // remember open sockets, close it when Ctrl+C Pressed.
        // This is the way to notify the outer to quit...
	openSockets[pointer]=socket_com;
	pointer++;

	char buf[128];

	debug("+++Reading from outer......\n");
	while(read(socket_com, buf,1)==1)
	{
       		//connect to the local service , port_service
        	socket_local = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        	if(socket_local <0) {
                	sys_log(info_log, "socket error (connect_socks4)\n");
                	return ;
        	}
        	saddr_local.sin_family=AF_INET;
        	saddr_local.sin_addr.s_addr=inet_addr("127.0.0.1");
        	saddr_local.sin_port =port_local;
	
        	//connect to outer port_com 
        	result= connect_nonb(socket_local, (struct sockaddr *)&saddr_local, sizeof(saddr_local),&timeout);
        	if ( result <0 ) {
            		close (socket_local);
            		return ;
        	}
	
		debug("connect to local service ok, port :%d\n",ntohs(port_local));
		
        	socket_com2 = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        	if(socket_com2 <0) {
                	sys_log(info_log, "socket error (connect_socks4)\n");
                	return ;
        	}
        	saddr_com2.sin_family=AF_INET;
        	saddr_com2.sin_addr.s_addr=ipAddress; //htonl(nbr_ip);
        	saddr_com2.sin_port =port_com;
        	
        	//connect to outer port_com 
        	result= connect_nonb(socket_com2, (struct sockaddr *)&saddr_com, sizeof(saddr_com),&timeout);
        	if ( result <0 ) {
            		close (socket_com2);
            		return ;
        	}
		struct in_addr addr;
		addr.s_addr=ipAddress;
		debug("connect to remote outer:%s ok, port :%d\n",inet_ntoa(addr),ntohs(port_com));
		debug("shuffling begin...\n");

                pthread_t tid;
                struct outer_thread_args * args;
                args=(struct outer_thread_args *) malloc(sizeof(struct outer_thread_args));
                //args->ip=socket_com2;
                args->port_com=socket_com2;
                args->port_local=socket_local;
 
		debug("Process comming request......\n");
                if(pthread_create(&tid,NULL, shuffle_thread_proccess, (void *) args))
		{
			//error on creating thread..
			close(socket_com);
			close(socket_com2);
			close(socket_local);
		}
		
		debug("Waiting to Read from outer......\n");
	}
	close(socket_com);	
}

//all args are in network order
int reverse_connect_outer(in_addr_t  ipAddress, int port_com, int port_local)
{
//For node outside NAT who want to setup a reverse connection to the node inside NAT firewall
// 1. setup a socket1 , bind , and listen on port port_com, and wait on ipAddress to connect;
// 2. accept the remote connect, client socket2;
// 3. setup a socket3 bind, listen on local port port_local,
// 4. if accept from local connection on port_local, relay between  socket2 and socket3 
// return 0 if success , or -1 if error.
	int result;

	struct in_addr inaddr;
	inaddr.s_addr=ntohl(ipAddress);
	debug("Begin reverse_connect_out ,remote ip= %s  port_com= %d  port_local=%d \n", 
	      inet_ntoa(inaddr), ntohs(port_com),ntohs(port_local) );
	pthread_t tid;
	struct outer_thread_args * args;
	args=(struct outer_thread_args *) malloc(sizeof(struct outer_thread_args));
	args->ip=ipAddress;
	args->port_com=port_com;
	args->port_local=port_local;

	if( pthread_create(&tid,NULL, outer_proccess, (void *) args))
		return -1;
		
	return 0;
}
void * outer_proccess(void * args)
{
	int port_com,port_local;
	in_addr_t ipAddress;
	int result;
	int socket_com, socket_local;
	int serviceBusy=0;
	int socket_inner; // Used for signaling between outer and inner, can not be close.
	int socket_new;   // When local request connected,  outer notify the inner, then inner set up this socket,
			  // used for relay data.
	int socket_caller;
	int serviceReady=0;

	int callerCome=0;
	
	struct sockaddr_in serverAddr, saddr_inner;
	struct sockaddr_in inner_addr, caller_addr;
	int len_inner,len_caller;

	
    	pthread_detach(pthread_self());
    	ipAddress = ((struct outer_thread_args *) args) -> ip;
    	port_com= ((struct outer_thread_args *) args) ->port_com;
    	port_local= ((struct outer_thread_args *) args) ->port_local;
    	free(args);

	//Setup a server socket, to listen on port_com, waiting for the inner to connect....
	
	socket_com=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(socket_com==-1){
	 	debug("outer_proccess: socket error\n");
		return ;
	}
	bzero(&serverAddr, sizeof(serverAddr));

	serverAddr.sin_family=AF_INET;
	serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
	serverAddr.sin_port=port_com;
	
	result=bind(socket_com, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
	if (result !=0) 
	{
		close(socket_com);
	 	debug("outer_proccess: bind port:%d error\n",ntohs(port_com));
		return ;	
	}

	if(listen(socket_com, 10)==-1) {
		close(socket_com);
	 	debug("outer_proccess: listen error\n");
		return ;
	}
	debug("listening on port_com:%d\n", ntohs(port_com));

	socket_inner=accept(socket_com, (struct sockaddr *)&inner_addr, &len_inner);

	debug("accept connect ok from inner, signaling is ok\n");
	serviceReady=1;

        //Setup a server socket, to listen on port_local, waiting for the local connect....

        socket_local=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(socket_local==-1){
		close(socket_com);
		close(socket_local);
	 	debug("outer_proccess: socket local error\n");
                return ;
        }
        struct sockaddr_in serviceAddr;
        bzero(&serverAddr, sizeof(serviceAddr));
        
        serviceAddr.sin_family=AF_INET;
        serviceAddr.sin_addr.s_addr=htonl(INADDR_ANY);
        serviceAddr.sin_port=port_local;

        result=bind(socket_local, (struct sockaddr *)&serviceAddr, sizeof(serviceAddr));
        if (result !=0) 
	{
		close(socket_com);
		close(socket_local);
	 	debug("outer_proccess: bind port:%d error\n",ntohs(port_local));
		return ;
	}

        if(listen(socket_local, 10)==-1) 
	{
		close(socket_com);
		close(socket_local);
		return ;		
	}
	debug("listening on port_local:%d\n", ntohs(port_local));

	fd_set rset;
	FD_ZERO(&rset);

	int maxfd=socket_local+1;
	char buf[128];

	serviceReady=0; 

	for(;;)
	{	
		debug("Listening on 2 ports: %d %d..\r\n\n", ntohs(port_com), ntohs(port_local));
		FD_SET(socket_local,&rset);
		FD_SET(socket_com, &rset);
		FD_SET(socket_inner, &rset);
		int nReady=select(maxfd, &rset,NULL, NULL, NULL); //  waiting forever 
		debug("\n after select ..\n");

		if(FD_ISSET(socket_local,&rset) )
		{
			socket_caller=accept(socket_local, (struct sockaddr*)&caller_addr, &len_caller);
			debug("Caller comes....\n");
		    /*
			if(!serviceReady)
			{
				// the inner have not connected now, please try later...
				close(socket_caller);
				debug("But service is not ready, close please....\n");
			}
			else
			{
		   */
				 callerCome=1;
				 buf[0]=1;          //notify the peer
				 debug("try to notify the inner ,begin write....\n");
				 if(write(socket_inner,buf,1)!=1)
				 {
					debug("write to inner error, maybe it quited, I 'll quit right now\n");	
					close(socket_inner);
					close(socket_local);
					break;
				 }
				debug("I have notifed the inner....\n");
			 //}	
		}
		if(FD_ISSET(socket_com, &rset))
		{
			//the inner connected to port_com	
			socket_new=accept(socket_com, (struct sockaddr *)&inner_addr, &len_inner);
			serviceReady=1; 
			debug("Inner connect.. service is ready..\n");
		}
		if(FD_ISSET(socket_inner,&rset) )
		{	
	       		debug("inner quit or die, I will break ...\n");
			close(socket_com);
			close(socket_inner);
			close(socket_local);
			break;
		}

		if(serviceReady && callerCome )
		{	
			debug("begin shuffling ....\n");
			//serviceBusy=1;
			//thread_t tid1;
			//tid
			//shuffle_thread_process(socket_caller,socket_inner);

	        	pthread_t tid;
        		struct outer_thread_args * args;
		        args=(struct outer_thread_args *) malloc(sizeof(struct outer_thread_args));
        		//args->ip=ipAddress;
        		args->port_com=socket_caller;
        		args->port_local=socket_new;
			
        		if( pthread_create(&tid,NULL, shuffle_thread_proccess, (void *) args)){
				close(socket_caller);
				close(socket_new);
                		//return ;
			}
			serviceReady=0;
			callerCome=0;	
		}
	}

	close(socket_inner);
	close(socket_local);
	pthread_exit(NULL);
}

void * shuffle_thread_proccess(void * args)
{

	
	int client, server;
        pthread_detach(pthread_self());
        //ipAddress = ((struct outer_thread_args *) args) -> ip;
        client= ((struct outer_thread_args *) args) ->port_com;
        server= ((struct outer_thread_args *) args) ->port_local;
        free(args);
	debug("In Shuffle_thread, Begin..");
	shuffle_bytes(client,server);
	close(client);
	close(server);
	pthread_exit(NULL);
	debug("In Shuffle_thread, End... and exited");
}


int
tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
{
        int                             listenfd, n;
        const int               on = 1;
        struct addrinfo hints, *res, *ressave;

        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_flags = AI_PASSIVE;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
	{
                printf("tcp_listen error for %s, %s: %s",
                                 host, serv, gai_strerror(n));
		return -1;
	}
        ressave = res;

        do {
                listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
                if (listenfd < 0)
                        continue;               /* error, try next one */

                setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
                if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
                        break;                  /* success */

                close(listenfd);        /* bind error, close and try next one */
        } while ( (res = res->ai_next) != NULL);

        if (res == NULL)        /* errno from final socket() or bind() */
	{
                printf("tcp_listen error for %s, %s", host, serv);
		return -1;
	}

        listen(listenfd, LISTENQ);

        if (addrlenp)
                *addrlenp = res->ai_addrlen;    /* return size of protocol address */

        freeaddrinfo(ressave);

        return(listenfd);
}
/* end tcp_listen */

int tcp_connect(const char *host, const char *serv)
{
        int                             sockfd, n;
        struct addrinfo hints, *res, *ressave;

        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
	{
                debug("tcp_connect error for: %s, %s: %s\n",
                                 host, serv, gai_strerror(n));
		return -1;
	}
        ressave = res;

        do {
                sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
                if (sockfd < 0)
                        continue;       /* ignore this one */

                if (connect_nonb(sockfd, res->ai_addr, res->ai_addrlen,&timeout) == 0)
                        break;          /* success */

                close(sockfd);  /* ignore this one */
        } while ( (res = res->ai_next) != NULL);

        if (res == NULL)        /* errno set from final connect() */
	{
                debug("tcp_connect error for %s, %s\n", host, serv);
		return -1;
	}
        freeaddrinfo(ressave);

        return(sockfd);
}
/* end tcp_connect */

int tcp_connect6(const char *host, const char *serv)
{
        int   sockfd, n;
        struct addrinfo hints, *res, *ressave;


	sockfd=-1;
        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
	{
                printf("tcp_connect error for %s, %s: %s",
                                 host, serv, gai_strerror(n));
		return -1;
	}
        ressave = res;

        do {
	   char ipstr[128];
	   struct sockaddr_in6 * addr6;
	   struct sockaddr_in * addr;

	   if(res->ai_family ==AF_INET6){
	   	addr6=(struct sockaddr_in6 *) res->ai_addr;
	   	inet_ntop(res->ai_family, &addr6->sin6_addr,ipstr, sizeof(ipstr));
	    }
	   else
	   {
	   	addr=(struct sockaddr_in *) res->ai_addr;
	   	inet_ntop(res->ai_family, &addr->sin_addr,ipstr, sizeof(ipstr));
           }
	   debug("IP address: %s, \n", ipstr); 
		
	   if(res->ai_family==AF_INET6) 
	   {
		
                sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
                if (sockfd < 0)
                        continue;       /* ignore this one */

                if (connect_nonb(sockfd, res->ai_addr, res->ai_addrlen,&timeout) == 0)
                        break;          /* success */

                close(sockfd);  /* ignore this one */
		sockfd=-1;
	   }
        } while ( (res = res->ai_next) != NULL);

        if (res == NULL)        /* errno set from final connect() */
	{
                debug("tcp_connect error for destination: %s, %s", host, serv);
		//return -1;
	}
        freeaddrinfo(ressave);

        return(sockfd);
}
/* end tcp_connect */
